package com.runa.rccprotocol.analysis;

import com.runa.monitor.comm.dto.newComm.*;
import com.runa.monitor.comm.entity.CommPanel;
import com.runa.monitor.comm.entity.CommValve;
import com.runa.monitor.comm.enums.FaultCode;
import com.runa.persistence.RepositoryServicePersis;
import com.runa.protocol.dto.IInterData;
import com.runa.protocol.util.FaultUtil;
import com.runa.protocol.analysis.MetaBufferAnalysis;
import com.runa.protocol.enums.ByteOrder;
import com.runa.protocol.util.ByteUtil;
import com.runa.rccprotocol.constant.AppFunCode;
import com.runa.rccprotocol.dto.param.*;
import com.runa.rccprotocol.dto.runa.RunaConcenAffirmDeny;
import com.runa.rccprotocol.dto.runa.RunaConcenResponseData;
import com.runa.rccprotocol.dto.equdata.*;
import com.runa.rccprotocol.dto.runa.RunaFrameSequence;
import com.runa.rccprotocol.dto.runa.RunaICStruct;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author zhuj
 * 瑞纳集中器解析器（内层）
 */
@Slf4j
public class RunaEquDataAnalysis extends MetaBufferAnalysis<RunaICStruct> {


    private RunaDeviceAnalysis runaDeviceAnalysis = new RunaDeviceAnalysis();

    private final SimpleDateFormat sdfym = new SimpleDateFormat("yyMM");

    private final SimpleDateFormat sdfymd = new SimpleDateFormat("yyMMdd");

    private final SimpleDateFormat sdfymdhm = new SimpleDateFormat("yyMMddHHmm");

    private final SimpleDateFormat sdfymdhms = new SimpleDateFormat("yyMMddHHmmss");

    private String netEquNo;

    @Override
    public int fill(RunaICStruct struct, int size, String netEquNo) {
        this.netEquNo = netEquNo;
        RunaConcenResponseData response = new RunaConcenResponseData();
        Object equData = null;
        switch (struct.getAfn()) {
            case ReqTerminalConfig:
                response.setCommand(AppFunCode.rnicAFN.ReqTerminalConfig);
                for (RunaFrameSequence.DataUnit unit : struct.getDataField()) {
                    equData = terminalVersionInfo(unit);
                    response.addRepData(equData);
                }
                break;
            case QueryParam:
                Object param = null;
                response.setCommand(AppFunCode.rnicAFN.QueryParam);
                for (RunaFrameSequence.DataUnit unit : struct.getDataField()) {
                    switch (unit.fn) {
                        case 1:
                            param = terminalCommParam(unit);
                            break;
                        case 9:
                            //TODO 暂不做
                            break;
                        case 3:
                            param = gprsIpPort(unit);
                            break;
                        case 84:
                            param = gprsDomain(unit);
                            break;
                        case 7:
                            param = ethernetIP(unit);
                            break;
                        case 193:
                            param = ethIpPort(unit);
                            break;
                        case 194:
                            param = ethDomain(unit);
                            break;
                        case 85:
                            param = areaAddressCode(unit);
                            break;
                        case 91:
                            response.addRepData(indexRecords(unit));
                            break;
                        case 92:
                            param = apportionSwitch(unit);
                            break;
                        case 100:
                            param = heatingSeason(unit);
                            break;
                        case 210:
                            param = publicUnitSchemeTemperature(unit);
                            break;
                        case 211:
                            param = publicUnitWeekDaySchemeSelect(unit);
                            break;
                        case 212:
                            param = publicUnitDiyHolidaySchemeSelect(unit);
                            break;
                        case 213:
                            param = pid(unit);
                            break;
                        case 215:
                            param = publicUnitOpeningLimit(unit);
                            break;
                        case 216:
                            param = publicUnitAdjustStrategy(unit);
                            break;
                        case 191:
                            param = readMeterPeriod(unit);
                            break;
                        case 192:
                            param = password(unit);
                            break;
                        default:
                    }
                    if (unit.fn != 91) {
//                        param.setIndex(unit.pn);
                        response.addRepData(param);
                    }
                }
                break;
            case RequestDataOne:
                response.setCommand(AppFunCode.rnicAFN.RequestDataOne);
                for (RunaFrameSequence.DataUnit unit : struct.getDataField()) {
                    switch (unit.fn) {
                        case 1:
                            break;
                        case 2:
                            equData = TerminalCalendarClock.getInstance(unit.data, 0);
                            break;
                        case 4:
                            equData = terminalReportOnOff(unit);
                            break;
                        case 10:
                            TerminalDayMonthCommFlow flow = new TerminalDayMonthCommFlow();
                            flow.setDayCommFlow(ByteUtil.byteToInt(unit.data, 0, ByteOrder.DCBA));
                            flow.setMonthCommFlow(ByteUtil.byteToInt(unit.data, 4, ByteOrder.DCBA));
                            equData = flow;
                            break;
                        case 12:
                            TerminalPowerOnResetTime onResetTime = new TerminalPowerOnResetTime();
                            onResetTime.setDayPowerOnMinute(ByteUtil.byteToShort(unit.data, 0, ByteOrder.BA).shortValue());
                            onResetTime.setDayResetTime(ByteUtil.byteToShort(unit.data, 2, ByteOrder.BA).shortValue());
                            onResetTime.setMonthPowerOnMinute(ByteUtil.byteToShort(unit.data, 4, ByteOrder.BA).shortValue());
                            equData = onResetTime;
                            break;
                        case 13:
                            TerminalRunTime runTime = new TerminalRunTime();
                            runTime.setSecond(ByteUtil.byteToInt(unit.data, 0, ByteOrder.DCBA));
                            equData = runTime;
                            break;
                        case 14:
                            equData = terminalGPRSStatus(unit);
                            break;
                        case 129:
                            equData = electricityMeterData(unit, 0);
                            break;
                        case 173:
                            equData = waterMeterData(unit, 0);
                            break;
                        case 174:
                            equData = heatMeterData(unit, 0);
                            break;
                        case 176:
                            equData = publicBuildingsOrUnitValveData(unit, 0);
                            break;
                        case 177:
                            equData = temperatureCollectorData(unit, 0);
                            break;
                        case 200:
                            equData = valveData(unit, 0);
                            break;
                        case 201:
                            equData = measurTemperaturePanelData(unit, 0);
                            break;
                        case 202:
                            equData = heatMeterRecordData(unit).getHeatMeterData();
                            break;
                        case 203:
                            equData = waterMeterRecordData(unit);
                            break;
                        case 204:
                            equData = electricityMeterRecordData(unit);
                            break;
                        case 205:
                            equData = valveRecordData(unit).getValveData();
                            break;
                        case 206:
                            equData = temperatureCollectorRecordData(unit);
                            break;
                        case 207:
                            equData = measurTemperaturePanelRecordData(unit).getMeasurTemperaturePanelData();
                            break;
                        case 208:
                            equData = publicBuildingsOrUnitValveRecordData(unit);
                            break;
                        case 248:
                            equData = extendCodeData(unit);
                            break;
                        default:
                    }
                    if (equData != null) {
//                        equData.setIndexNo(unit.pn);各自添加
                        response.addRepData(equData);
                    }
                }
                break;
            case RequestDataTwo:
                response.setCommand(AppFunCode.rnicAFN.RequestDataTwo);
                for (RunaFrameSequence.DataUnit unit : struct.getDataField()) {
                    switch (unit.fn) {
                        case 49:
                            equData = terminalDayPowerTime(unit);
                            break;
                        case 51:
                            equData = terminalMonthPowerTime(unit);
                            break;
                        case 53:
                            equData = terminalDayCommFlow(unit);
                            break;
                        case 54:
                            equData = terminalMonthCommFlow(unit);
                            break;
                        case 219:
                            equData = heatMeterDayFreezeData(unit);
                            break;
                        case 220:
                            equData = heatMeterMonthFreezeData(unit);
                            break;
                        case 225:
                            equData = heatMeterHourFreezeData(unit);
                            break;
                        default:
                    }
                    if (equData != null) {
//                        equData.setIndexNo(unit.pn);
                        response.addRepData(equData);
                    }
                }
                break;
            case RequestDataThree:
                response.setCommand(AppFunCode.rnicAFN.RequestDataThree);
                for (RunaFrameSequence.DataUnit unit : struct.getDataField()) {
                    if (unit.fn == 3) {
                        EventRecordData recordData = new EventRecordData();
                        try {
                            recordData.setDate(sdfymdhms.parse(ByteUtil.bcd2str(unit.data, 0, 6, false)));
                            recordData.setRestore((unit.data[7] >> 7) == 1);
                            recordData.setErc(EventRecordData.ERC.valueOf(unit.data[6] + ((unit.data[7] & 0x7f) << 8)));
                            recordData.setSource(ByteUtil.byteToShort(unit.data, 8, ByteOrder.BA));
                            if (recordData.getErc() == EventRecordData.ERC.HeatMeterFlowOutLimit) {
                                recordData.setContent(heatMeterData(unit, 11));
                            }

                            recordData.setIndexNo(unit.pn);
                            response.addRepData(recordData);
                        } catch (Exception e) {
                            //TODO
                        }
                    }
                }
                break;
            case DataForward:
                for (RunaFrameSequence.DataUnit unit : struct.getDataField()) {
                    if (unit.fn == 1) {
                        TransForwardReply reply = new TransForwardReply();
                        reply.setPort(unit.data[0]);
                        int k = ByteUtil.byteToUShort(unit.data, 1, ByteOrder.BA);
                        byte[] bs = new byte[k];
                        System.arraycopy(unit.data, 3, bs, 0, k);
                        reply.setContent(bs);
                        reply.setIndexNo(unit.pn);
                        runaDeviceAnalysis.fill(bs, 0, struct.getArea().toString());
                        IInterData gain = runaDeviceAnalysis.gain();
                        response.setCommand(AppFunCode.rnicAFN.DataForward);
                        if (gain instanceof RunaConcenAffirmDeny) {
                            responseData = (RunaConcenAffirmDeny)gain;
                            return 0;
                        } else if (gain instanceof RunaConcenResponseData) {
                            RunaConcenResponseData r = (RunaConcenResponseData)gain;
                            for (Object d : r.getData()) {
                                response.addRepData(d);
                            }
                        }
                    }
                }
                break;
            default:
        }
        response.setAddress(struct.getArea().toString());
        responseData = response;
        return 0;
    }

    public ConcenReportData concenReportData(byte[] data, int offset) {
        ConcenReportData concenReportData = new ConcenReportData();
        try {
            concenReportData.setDate(sdfymdhms.parse(ByteUtil.bcd2str(data, offset, 6, false)));
            concenReportData.setSdCardStatus1(data[6 + offset] & 0xff);
            concenReportData.setSdCardStatus2(data[7 + offset] & 0xff);
            concenReportData.setMbusStatus(data[8 + offset] & 0xff);
            //保留一字节
            concenReportData.setCsq(data[10 + offset] & 0xff);
            //保留两字节
            concenReportData.setTime(sdfymdhm.parse(ByteUtil.bcd2str(data, 13 + offset, 5, false)));
            concenReportData.setElectric(ByteUtil.byteToFloat(data, 18 + offset, ByteOrder.DCBA));
            return concenReportData;
        } catch (Exception e) {
            //TODO

        }
        return null;
    }

    private TerminalCommParam terminalCommParam(RunaFrameSequence.DataUnit unit) {
        TerminalCommParam terminalCommParam = new TerminalCommParam();
        try {
            terminalCommParam.setRTS(unit.data[0] & 0xff);
            terminalCommParam.setDelayTime(unit.data[1] & 0xff);
            terminalCommParam.setTimeoutReset(ByteUtil.byteToShort(unit.data, 2, ByteOrder.BA));
            terminalCommParam.setSign(unit.data[4]);
            terminalCommParam.setHeartPeriod(unit.data[5] & 0xff);
            return terminalCommParam;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private GPRSIpPort gprsIpPort(RunaFrameSequence.DataUnit unit) {
        GPRSIpPort gprsIpPort = new GPRSIpPort();
        try {
            gprsIpPort.setIp1((unit.data[0] & 0xff) + "."
                    + (unit.data[1] & 0xff) + "." + (unit.data[2] & 0xff) + "." + (unit.data[3] & 0xff));
            gprsIpPort.setPort1(ByteUtil.byteToShort(unit.data, 4, ByteOrder.BA));
            gprsIpPort.setIp2((unit.data[6] & 0xff) + "."
                    + (unit.data[7] & 0xff) + "." + (unit.data[8] & 0xff) + "." + (unit.data[9] & 0xff));
            gprsIpPort.setPort1(ByteUtil.byteToShort(unit.data, 10, ByteOrder.BA));
            gprsIpPort.setAPN(new String(unit.data, 12, 16));
            return gprsIpPort;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private GPRSDomain gprsDomain(RunaFrameSequence.DataUnit unit) {
        GPRSDomain gprsDomain = new GPRSDomain();
        try {
            gprsDomain.setDomain1(new String(unit.data, 0, 32));
            gprsDomain.setDomain2(new String(unit.data, 64, 32));
            return gprsDomain;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private EthernetIP ethernetIP(RunaFrameSequence.DataUnit unit) {
        EthernetIP ethernetIP = new EthernetIP();
        try {
            ethernetIP.setIp((unit.data[0] & 0xff) + "."
                    + (unit.data[1] & 0xff) + "." + (unit.data[2] & 0xff) + "." + (unit.data[3] & 0xff));
            ethernetIP.setSubnet((unit.data[4] & 0xff) + "."
                    + (unit.data[5] & 0xff) + "." + (unit.data[6] & 0xff) + "." + (unit.data[7] & 0xff));
            ethernetIP.setGateway((unit.data[8] & 0xff) + "."
                    + (unit.data[9] & 0xff) + "." + (unit.data[10] & 0xff) + "." + (unit.data[11] & 0xff));
            //TODO 剩余保留字节未作处理
            return ethernetIP;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private ETHIpPort ethIpPort(RunaFrameSequence.DataUnit unit) {
        ETHIpPort ethIpPort = new ETHIpPort();
        try {
            ethIpPort.setIp1((unit.data[0] & 0xff) + "."
                    + (unit.data[1] & 0xff) + "." + (unit.data[2] & 0xff) + "." + (unit.data[3] & 0xff));
            ethIpPort.setPort1(ByteUtil.byteToShort(unit.data, 4, ByteOrder.BA));
            ethIpPort.setIp2((unit.data[6] & 0xff) + "."
                    + (unit.data[7] & 0xff) + "." + (unit.data[8] & 0xff) + "." + (unit.data[9] & 0xff));
            ethIpPort.setPort2(ByteUtil.byteToShort(unit.data, 10, ByteOrder.BA));
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private ETHDomain ethDomain(RunaFrameSequence.DataUnit unit) {
        ETHDomain ethDomain = new ETHDomain();
        try {
            ethDomain.setDomain1(new String(unit.data, 0, 32));
            ethDomain.setDomain2(new String(unit.data, 64, 32));
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private AreaAddressCode areaAddressCode(RunaFrameSequence.DataUnit unit) {
        AreaAddressCode code = new AreaAddressCode();
        try {
            code.setFactory(ByteUtil.byteToHexString(unit.data, 0, 2));
            code.setArea(ByteUtil.bcd2str(unit.data, 2, 2, true));
            code.setAddress(ByteUtil.byteToHexString(unit.data, 4, 2));
            return code;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private List<Archive> indexRecords(RunaFrameSequence.DataUnit unit) {
        List<Archive> indexRecords = new ArrayList<>();
        int number = ByteUtil.byteToShort(unit.data, 0, ByteOrder.BA);
        for (int i = 0; i < number; i++) {
            int offset = i * 22;
            Archive archive = new Archive();
            archive.setIndex(Integer.valueOf(ByteUtil.byteToShort(unit.data, 4 + offset, ByteOrder.BA)));
            archive.setPort(Integer.valueOf(unit.data[6 + offset]));
            archive.setProtocol(Integer.valueOf(unit.data[7 + offset]));
            archive.setSerial(ByteUtil.bcd2str(unit.data, 8 + offset, 6, false).toUpperCase());
            archive.setSid(ByteUtil.bcd2str(unit.data, 14 + offset, 6, false));
            archive.setHeatArea((float) ByteUtil.bcdToInt(unit.data, 20 + offset, 2, false) / 10);
            byte fs = unit.data[22 + offset];
            archive.setApportion((fs & 1) == 1);
            archive.setSmallClass(fs >> 4 & 0x0f);
            archive.setType(Integer.valueOf(unit.data[23 + offset]));
            indexRecords.add(archive);
        }
        return indexRecords;
    }

    private ApportionSwitch apportionSwitch(RunaFrameSequence.DataUnit unit) {
        ApportionSwitch apportionSwitch = new ApportionSwitch();
        try {
            apportionSwitch.setAppSwitch(unit.data[0]);
            apportionSwitch.setAppPeriod(unit.data[1]);
            apportionSwitch.setAppType(unit.data[2]);
            //TODO 剩余17预留字节未处理
            return apportionSwitch;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private HeatingSeason heatingSeason(RunaFrameSequence.DataUnit unit) {
        HeatingSeason season = new HeatingSeason();
        try {
            season.setStartDay(ByteUtil.bcdToInt(unit.data, 0, 1, false));
            season.setStartMonth(ByteUtil.bcdToInt(unit.data, 1, 1, false));
            season.setEndDay(ByteUtil.bcdToInt(unit.data, 2, 1, false));
            season.setEndMonth(ByteUtil.bcdToInt(unit.data, 3, 1, false));
            return season;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private PublicUnitSchemeTemperature publicUnitSchemeTemperature(RunaFrameSequence.DataUnit unit) {
        PublicUnitSchemeTemperature temperature = new PublicUnitSchemeTemperature();
        try {
            temperature.setCount(unit.data[0] & 0xff);
            for (int i = 0; i < temperature.getCount(); i++) {
                int offset = i * 37;
                temperature.setSchemes(new PublicUnitSchemeTemperature.Scheme(
                        unit.data[1 + offset],
                        ByteUtil.bcdToInt(unit.data, 3 + offset, 1, false) + ":" + ByteUtil.bcdToInt(unit.data, 2 + offset, 1, false),
                        (float) ByteUtil.bcdToInt(unit.data, 4 + offset, 4, false) / 100,
                        ByteUtil.bcdToInt(unit.data, 9 + offset, 1, false) + ":" + ByteUtil.bcdToInt(unit.data, 8 + offset, 1, false),
                        (float) ByteUtil.bcdToInt(unit.data, 10 + offset, 4, false) / 100,
                        ByteUtil.bcdToInt(unit.data, 15 + offset, 1, false) + ":" + ByteUtil.bcdToInt(unit.data, 14 + offset, 1, false),
                        (float) ByteUtil.bcdToInt(unit.data, 16 + offset, 4, false) / 100,
                        ByteUtil.bcdToInt(unit.data, 21 + offset, 1, false) + ":" + ByteUtil.bcdToInt(unit.data, 20 + offset, 1, false),
                        (float) ByteUtil.bcdToInt(unit.data, 22 + offset, 4, false) / 100,
                        ByteUtil.bcdToInt(unit.data, 27 + offset, 1, false) + ":" + ByteUtil.bcdToInt(unit.data, 26 + offset, 1, false),
                        (float) ByteUtil.bcdToInt(unit.data, 28 + offset, 4, false) / 100,
                        ByteUtil.bcdToInt(unit.data, 33 + offset, 1, false) + ":" + ByteUtil.bcdToInt(unit.data, 32 + offset, 1, false),
                        (float) ByteUtil.bcdToInt(unit.data, 34 + offset, 4, false) / 100
                ), i);
            }
            return temperature;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private PublicUnitWeekDaySchemeSelect publicUnitWeekDaySchemeSelect(RunaFrameSequence.DataUnit unit) {
        PublicUnitWeekDaySchemeSelect schemeSelect = new PublicUnitWeekDaySchemeSelect();
        try {
            schemeSelect.setMonday(unit.data[0] & 0xff);
            schemeSelect.setTuesday(unit.data[1] & 0xff);
            schemeSelect.setWednesday(unit.data[2] & 0xff);
            schemeSelect.setThursday(unit.data[3] & 0xff);
            schemeSelect.setFriday(unit.data[4] & 0xff);
            schemeSelect.setSaturday(unit.data[5] & 0xff);
            schemeSelect.setSunday(unit.data[6] & 0xff);
            return schemeSelect;
        } catch (Exception e) {
            //tODO
        }
        return null;
    }

    private PublicUnitDiyHolidaySchemeSelect publicUnitDiyHolidaySchemeSelect(RunaFrameSequence.DataUnit unit) {
        PublicUnitDiyHolidaySchemeSelect schemeSelect = new PublicUnitDiyHolidaySchemeSelect();
        try {
            schemeSelect.setCount(unit.data[0]);
            for (int i = 0; i < schemeSelect.getCount(); i++) {
                int offset = i * 8;
                schemeSelect.setSchemeSelects(new PublicUnitDiyHolidaySchemeSelect.SchemeSelect(
                        unit.data[1 + offset] & 0xff, "20" + ByteUtil.bcd2str(unit.data, 2 + offset, 3, false),
                        "20" + ByteUtil.bcd2str(unit.data, 5 + offset, 3, false), unit.data[8 + offset]
                ), i);
            }
            return schemeSelect;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private BuildValveParamVO pid(RunaFrameSequence.DataUnit unit) {
        BuildValveParamVO buildValveParamVO = new BuildValveParamVO();
        try {
            buildValveParamVO.setP(String.valueOf(unit.data[0]));
            buildValveParamVO.setI(String.valueOf(unit.data[1]));
            buildValveParamVO.setD(String.valueOf(unit.data[2]));
            return buildValveParamVO;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private BuildValveParamVO publicUnitOpeningLimit(RunaFrameSequence.DataUnit unit) {
        BuildValveParamVO buildValveParamVO = new BuildValveParamVO();
        try {
//            limit.setDownLimit(unit.data[0] & 0xff);
            buildValveParamVO.setValveDL(Float.valueOf(unit.data[0] & 0xff));
//            limit.setUpLimit(unit.data[1] & 0xff);
            buildValveParamVO.setValveUL(Float.valueOf(unit.data[1] & 0xff));
            return buildValveParamVO;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private BuildValveParamVO publicUnitAdjustStrategy(RunaFrameSequence.DataUnit unit) {
        BuildValveParamVO buildValveParamVO = new BuildValveParamVO();
        try {
//            strategy.setHandAuto(unit.data[0] & 0xff);
//            strategy.setControlObject(unit.data[1] & 0xff);
            buildValveParamVO.setCtrlType(ValveVO.geneModel(unit.data[1] & 0xff));
//            strategy.setSetting((float) ByteUtil.bcdToInt(unit.data, 2, 4, false) / 100);
            buildValveParamVO.setTargetVal((float) ByteUtil.bcdToInt(unit.data, 2, 4, false) / 100);
            return buildValveParamVO;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private ReadMeterPeriod readMeterPeriod(RunaFrameSequence.DataUnit unit) {
        ReadMeterPeriod period = new ReadMeterPeriod();
        try {
            period.setPeriod(ByteUtil.byteToShort(unit.data, 0, ByteOrder.BA));
            period.setOverTimeRate(unit.data[2]);
            period.setReadTimeDelay(unit.data[3]);
            return period;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private Password password(RunaFrameSequence.DataUnit unit) {
        Password password = new Password();
        try {
            password.setPassword(new String(unit.data));
            return password;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private TerminalVersionInfo terminalVersionInfo(RunaFrameSequence.DataUnit unit)  {
        TerminalVersionInfo info = null;
        try {
            info = new TerminalVersionInfo();
            info.setFactoryNo(new String(unit.data, 0, 4));
            info.setEquSerial(new String(unit.data, 4, 8));
            String version = new String(unit.data, 12, 4);
            if (StandardCharsets.US_ASCII.newEncoder().canEncode(version)) {
                info.setTerminalSoftVsn(version);
            }
            info.setTerminalSoftDay(sdfymd.parse(ByteUtil.bcd2str(unit.data, 16, 3, false)));
            info.setTerminalConfigCapCode(new String(unit.data, 19, 11));
            info.setTerminalCommProtoVsn(new String(unit.data, 30, 4));
            info.setTerminalHardVsn(new String(unit.data, 34, 4));
            info.setTerminalHardDay(sdfymd.parse(ByteUtil.bcd2str(unit.data, 38, 3, false)));
        } catch (Exception e) {
            //TODO
        }
        return info;
    }

    private TerminalReportOnOff terminalReportOnOff(RunaFrameSequence.DataUnit unit) {
        TerminalReportOnOff onOff = new TerminalReportOnOff();
        onOff.setStandby1((byte) (unit.data[0] >>> 6));
        onOff.setStandby2((byte) ((unit.data[0] >>> 4) & 3));
        onOff.setStandby3((byte) ((unit.data[0] >>> 2) & 3));
        if ((unit.data[0] & 3) == 0 || (unit.data[0] & 3) == 3) {
            onOff.setState(TerminalReportOnOff.State.Invalid);
        } else if ((unit.data[0] & 3) == 1) {
            onOff.setState(TerminalReportOnOff.State.InitReport);
        } else if ((unit.data[0] & 3) == 2) {
            onOff.setState(TerminalReportOnOff.State.NoInitReport);
        }
        return onOff;
    }

    private TerminalGPRSStatus terminalGPRSStatus(RunaFrameSequence.DataUnit unit) {
        TerminalGPRSStatus gprsStatus = new TerminalGPRSStatus();
        gprsStatus.setModemStatus(unit.data[0] == 1);
        gprsStatus.setModemType(unit.data[1] == 1 ? TerminalGPRSStatus.MODEM.CDMA : TerminalGPRSStatus.MODEM.GPRS);
        gprsStatus.setModemSimStatus(unit.data[2] == 1);
        gprsStatus.setModemSignalIntensity(unit.data[3]);
        gprsStatus.setModemSignalIntensityLevel(unit.data[4]);
        gprsStatus.setModemRegisterNet(unit.data[5] == 1);
        gprsStatus.setModemDialStatus(unit.data[6] == 1);
        gprsStatus.setMasterLinkStatus1(unit.data[7] == 1);
        gprsStatus.setMasterLoginStatus1(unit.data[8] == 1);
        gprsStatus.setMasterLinkStatus2(unit.data[9] == 1);
        gprsStatus.setMasterLoginStatus2(unit.data[10] == 1);
        return gprsStatus;
    }

    private ElectricityMeterData electricityMeterData(RunaFrameSequence.DataUnit unit, int offset) {
        ElectricityMeterData meterData = new ElectricityMeterData();
        try {
            meterData.setReadDate(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
            meterData.setTotal(ByteUtil.bcdToInt(unit.data, 6 + offset, 5, false) / 10000);
            meterData.setCusp(ByteUtil.bcdToInt(unit.data, 11 + offset, 5, false) / 10000);
            meterData.setPeak(ByteUtil.bcdToInt(unit.data, 16 + offset, 5, false) / 10000);
            meterData.setMean(ByteUtil.bcdToInt(unit.data, 21 + offset, 5, false) / 10000);
            meterData.setTrough(ByteUtil.bcdToInt(unit.data, 26 + offset, 5, false) / 10000);
            return meterData;
        } catch (ParseException e) {
            //TODO

        }
        return null;
    }

    private WaterMeterData waterMeterData(RunaFrameSequence.DataUnit unit, int offset) {
        WaterMeterData meterData = new WaterMeterData();
        try {
            meterData.setReadDate(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
            meterData.setDayAccFow(ByteUtil.byteToInt(unit.data, 5 + offset, ByteOrder.ABCD) * 0.01);
            meterData.setAccFlow(ByteUtil.byteToInt(unit.data, 9 + offset, ByteOrder.ABCD) * 0.01);
            meterData.setInstantFlow(ByteUtil.byteToInt(unit.data, 13 + offset, ByteOrder.ABCD) * 0.01);
            meterData.setMeterDate(TerminalCalendarClock.getInstance(unit.data, 17 + offset));
            meterData.setMeterStatus(ByteUtil.bcdToInt(unit.data, 23 + offset, 2, true));
            return meterData;
        } catch (ParseException e) {
            //TODO

        }
        return null;
    }

    private HotMeterVO heatMeterData(RunaFrameSequence.DataUnit unit, int offset) {
        try {
            HotMeterVO hotMeterVO = new HotMeterVO();
            hotMeterVO.setIndexNo(unit.pn);
            hotMeterVO.setMeterDate(TerminalCalendarClock.getInstance(unit.data, 36 + offset).toDate());
            hotMeterVO.setReadDate(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
            hotMeterVO.setPower((float) (ByteUtil.bcdToInt(unit.data, 13 + offset, 4, false) * 0.01));
            hotMeterVO.setFlow((float) (ByteUtil.bcdToInt(unit.data, 17 + offset, 4, false) * 0.01));
            hotMeterVO.setAccFlow((float) (ByteUtil.bcdToInt(unit.data, 21 + offset, 4, false) * 0.01));
            hotMeterVO.setAccHeat((float) (ByteUtil.bcdToInt(unit.data, 9 + offset, 4, false) * 0.01));
            hotMeterVO.setSupplyTemp((float) (ByteUtil.bcdToInt(unit.data, 25 + offset, 3, false) * 0.01));
            hotMeterVO.setReturnTemp((float) (ByteUtil.bcdToInt(unit.data, 29 + offset, 3, false) * 0.01));
            if ((unit.data[28 + offset] & 1) == 1) {
                hotMeterVO.setAccHeat(hotMeterVO.getAccHeat() * 1000);
            }
            if ((unit.data[32 + offset] & 1) == 1) {
                hotMeterVO.setAccFlow(hotMeterVO.getAccFlow() * 1000);
            }
            hotMeterVO.setWorkTime(ByteUtil.bcdToInt(unit.data, 33 + offset, 3, false));
            hotMeterVO.setStatus(ByteUtil.byteToHexString(unit.data, 42 + offset, 2));
            if ("FFFF".equals(hotMeterVO.getStatus())) {
                hotMeterVO.setSuccess(false);
//                hotMeterVO.setFaultCode(Collections.singletonList(NetFaultCode.DEVICE_NOT_READ.getName()));
            } else {
                hotMeterVO.setFaultCode(heatMeterFaults(hotMeterVO.getStatus()));
            }
//            HeatMeterData meterData = new HeatMeterData();
//            meterData.setReadDate(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
//            meterData.setSettDateHeatColdVal((float) ByteUtil.bcdToInt(unit.data, 5+offset, 4, false) / 100);
//            meterData.setAccHeat((float) ByteUtil.bcdToInt(unit.data, 9+offset, 4, false) / 100);
//            meterData.setPower((float) ByteUtil.bcdToInt(unit.data, 13+offset, 4, false) / 100);
//            meterData.setInstantFlow((float) ByteUtil.bcdToInt(unit.data, 17+offset, 4, false) / 100);
//            meterData.setAccFlow((float) ByteUtil.bcdToInt(unit.data, 21+offset, 4, false) / 100);
//            meterData.setInTemperature((float) ByteUtil.bcdToInt(unit.data, 25+offset, 3, false) / 100);
//            if ((unit.data[28+offset] & 1) == 1) {
//                meterData.setAccHeat(meterData.getAccHeat() * 1000);
//            }
//            if ((unit.data[28+offset] >> 4 & 1) == 1) {
//                meterData.setSettDateHeatColdVal(meterData.getSettDateHeatColdVal() * 1000);
//            }
//            meterData.setOutTemperature((float) ByteUtil.bcdToInt(unit.data, 29, 3, false) / 100);
//            if ((unit.data[32+offset] & 1) == 1) {
//                meterData.setAccFlow(meterData.getAccFlow() * 1000);
//            }
//            meterData.setWorkTime(ByteUtil.bcdToInt(unit.data, 33+offset, 3, false));
//            meterData.setMeterDate(TerminalCalendarClock.getInstance(unit.data, 36+offset));
//            meterData.setMeterStatus(ByteUtil.byteToHexString(unit.data, 42+offset, 2));
//            if ("FFFF".equals(meterData.getMeterStatus())) {
//                meterData.setSuccess(false);
//            } else {
//                meterData.setFaults(heatMeterFaults(meterData.getMeterStatus()));
//            }
            return hotMeterVO;
        } catch (ParseException e) {
            //TODO

        }
        return null;
    }

    public static List<String> heatMeterFaults(String status) {
        int hexCode = Integer.parseInt(status, 16);
        ArrayList<String> list = new ArrayList<>();
        StringBuilder faults = new StringBuilder();
        if ((hexCode >> 10 & 1) == 1) {
//            faults.append("电池欠压,");
            list.add("电池欠压");
        }
        if ((hexCode >> 7 & 1) == 1) {
//            faults.append("无水,");
            list.add("无水");
        }
        if ((hexCode >> 6 & 1) == 1) {
//            faults.append("逆流,");
            list.add("水逆流");
        }
        if ((hexCode >> 2 & 1) == 1) {
//            faults.append("回水故障,");
            list.add("回水故障");
        }
        if ((hexCode >> 1 & 1) == 1) {
//            faults.append("进水故障,");
            list.add("进水故障");
        }
        return list;
    }


    private ValveVO publicBuildingsOrUnitValveData(RunaFrameSequence.DataUnit unit, int offset) {
//        PublicBuildingsOrUnitValveData valveData = new PublicBuildingsOrUnitValveData();
        ValveVO valveVO = new ValveVO();
        try {
            valveVO.setIndex(unit.pn);
            valveVO.setValveDate(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
//            valveData.setDate(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
//            valveData.setReadDate(new Date());
            valveVO.setReadTime(new Date());
            //由于硬件协议最初存在bug，但是现场老设备并没有升级，做了这个操蛋的兼容
            boolean valveComm = true;
            if ((unit.data[5 + offset] & 0xff) == 0xff && (unit.data[6 + offset] & 0xff) == 0xff) {
//                valveData.setStatus("FFFF");
                valveVO.setStatus("FFFF");
//                valveData.setValveComm(false);
//                valveVO.setStatus("FFFF");
                valveVO.setFaultCode(Collections.singletonList(FaultCode.WIRELESS_ABNORMAL.getName()));

            } else {
//                valveData.setStatus(ByteUtil.byteToHexString(unit.data, 7 + offset, 2));
                valveVO.setStatus(ByteUtil.byteToHexString(unit.data, 7 + offset, 2));
//                valveData.setCurrOpenValve(ByteUtil.bcdToInt(unit.data, 5 + offset, 2, false));
                valveVO.setActOpen((float) ByteUtil.bcdToInt(unit.data, 5 + offset, 2, false));
                if ((unit.data[7 + offset] & 0xff) == 0xff && (unit.data[8 + offset] & 0xff) == 0xff) {
//                    valveData.setValveComm(false);
                } else {
                    //现场有第一个字节为0，不验证
//                    valveComm = (unit.data[7 + offset] & 1) == 1 && unit.data.length >= 57 + offset;

//                    valveData.setSchemeTime((unit.data[8 + offset] & 1) == 1);
//                    valveData.setValveComm((unit.data[7 + offset] & 1) == 1 && unit.data.length >= 57 + offset);
                }
            }
//            valveData.setOutOpenValve(ByteUtil.bcdToInt(unit.data, 9 + offset, 2, false));
            valveVO.setOuputOpen((float) ByteUtil.bcdToInt(unit.data, 9 + offset, 2, false));
//            valveData.setHandAuto(unit.data[11 + offset] & 0xff);
//            valveData.setParamSelect(unit.data[12 + offset] & 0xff);
            if ((unit.data[16 + offset] & 0x80) != 0x80) {
//                valveData.setSetValue((float) ByteUtil.bcdToInt(unit.data, 13 + offset, 4, false) / 100);
                valveVO.setRemoteSet((float) ByteUtil.bcdToInt(unit.data, 13 + offset, 4, false) / 100);
            }
            if ((unit.data[20 + offset] & 0x80) != 0x80) {
//                valveData.setCurrValue((float) ByteUtil.bcdToInt(unit.data, 17 + offset, 4, false) / 100);
//                valveVO.setActValue((float) ByteUtil.bcdToInt(unit.data, 17 + offset, 4, false) / 100);
                valveVO.setActOpen((float) ByteUtil.bcdToInt(unit.data, 17 + offset, 4, false) / 100);
            }

//            if (!((unit.data[7 + offset] & 0xff) == 0xff && (unit.data[8 + offset] & 0xff) == 0xff)) {
            if ("FFFF".equals(valveVO.getStatus()) || !valveComm) {
                valveVO.setSuccess(false);
            }
            if (valveComm) {
                try {//兼容无此数据设备
                    valveVO.setSupplyTemp((float) ByteUtil.byteToShort(unit.data, 21 + offset, ByteOrder.BA) / 100);
                    valveVO.setReturnTemp((float) ByteUtil.byteToShort(unit.data, 23 + offset, ByteOrder.BA) / 100);
                    valveVO.setEnvTemp((float) ByteUtil.byteToShort(unit.data, 25 + offset, ByteOrder.BA) / 100);
                    valveVO.setSupplyPre((float) ByteUtil.byteToShort(unit.data, 27 + offset, ByteOrder.BA) / 1000);
                    valveVO.setBehindPre((float) ByteUtil.byteToShort(unit.data, 29 + offset, ByteOrder.BA) / 1000);
                    valveVO.setFrontPre((float) ByteUtil.byteToShort(unit.data, 31 + offset, ByteOrder.BA) / 1000);
                    valveVO.setFlow(ByteUtil.byteToFloat(unit.data, 33 + offset, ByteOrder.ABCD));
                    valveVO.setPower(ByteUtil.byteToFloat(unit.data, 37 + offset, ByteOrder.ABCD));
                    valveVO.setLR(ByteUtil.byteToShort(unit.data, 41 + offset, ByteOrder.BA) == 1 ? ValveVO.LREnum.远程 : ValveVO.LREnum.本地);
//                valveData.setControlStrategy(ByteUtil.byteToShort(unit.data, 43 + offset, ByteOrder.BA));
                    valveVO.setModel(ValveVO.geneModel(Integer.valueOf(ByteUtil.byteToShort(unit.data, 43 + offset, ByteOrder.BA))));
//                valveData.setSetPressureDiff(ByteUtil.byteToFloat(unit.data, 45 + offset, ByteOrder.ABCD));
//                valveData.setSetOutTemperature(ByteUtil.byteToFloat(unit.data, 49 + offset, ByteOrder.ABCD));
//                valveData.setSetTemperatureDiff(ByteUtil.byteToFloat(unit.data, 53 + offset, ByteOrder.ABCD));
//                valveData.setRnStatus(ByteUtil.byteToShort(unit.data, 57 + offset, ByteOrder.AB) & 0xff);
                    int rn = ByteUtil.byteToShort(unit.data, 57 + offset, ByteOrder.AB) & 0xff;
                    valveVO.setStatus(ByteUtil.byteToHexString(unit.data, 57 + offset, 2));
                    ArrayList<String> list = new ArrayList<>();
//                StringBuilder faults = new StringBuilder();
                    if ((rn & 1) == 1
                            || (rn >> 1 & 1) == 1
                            || (rn >> 4 & 1) == 1) {
//                    faults.append("阀门异常,");
                        list.add("阀门异常");
                    }
                    if ((rn >> 8 & 1) == 1) {
//                    faults.append("进水故障,");
                        list.add("进水故障");
                    }
                    if ((rn >> 9 & 1) == 1) {
//                    faults.append("回水故障");
                        list.add("回水故障");
                    }
                    valveVO.setFaultCode(list);
                } catch (Exception e) {
                    return valveVO;
                }
            }
            return valveVO;
        } catch (Exception e) {
            //TODO

        }
        return null;
    }

    private TemperatureCollectorData temperatureCollectorData(RunaFrameSequence.DataUnit unit, int offset) {
        TemperatureCollectorData data = new TemperatureCollectorData();
        try {
            data.setDate(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
            data.getMeasurePoints()[0] = new TemperatureCollectorData.MeasurePoint(
                    ByteUtil.byteToInt(unit.data, 5 + offset, ByteOrder.ABCD),
                    ByteUtil.bcdToInt(unit.data, 29 + offset, 2, false) / 100,
                    unit.data[30 + offset] & 0xff, unit.data[31 + offset]
            );
            data.getMeasurePoints()[1] = new TemperatureCollectorData.MeasurePoint(
                    ByteUtil.byteToInt(unit.data, 9 + offset, ByteOrder.ABCD),
                    ByteUtil.bcdToInt(unit.data, 32 + offset, 2, false) / 100,
                    unit.data[33 + offset] & 0xff, unit.data[34 + offset]
            );
            data.getMeasurePoints()[2] = new TemperatureCollectorData.MeasurePoint(
                    ByteUtil.byteToInt(unit.data, 13 + offset, ByteOrder.ABCD),
                    ByteUtil.bcdToInt(unit.data, 35 + offset, 2, false) / 100,
                    unit.data[36 + offset] & 0xff, unit.data[37 + offset]
            );
            data.getMeasurePoints()[3] = new TemperatureCollectorData.MeasurePoint(
                    ByteUtil.byteToInt(unit.data, 17 + offset, ByteOrder.ABCD),
                    ByteUtil.bcdToInt(unit.data, 38 + offset, 2, false) / 100,
                    unit.data[39 + offset] & 0xff, unit.data[40 + offset]
            );
            data.getMeasurePoints()[4] = new TemperatureCollectorData.MeasurePoint(
                    ByteUtil.byteToInt(unit.data, 20 + offset, ByteOrder.ABCD),
                    ByteUtil.bcdToInt(unit.data, 41 + offset, 2, false) / 100,
                    unit.data[42 + offset] & 0xff, unit.data[43 + offset]
            );
            data.getMeasurePoints()[5] = new TemperatureCollectorData.MeasurePoint(
                    ByteUtil.byteToInt(unit.data, 23 + offset, ByteOrder.ABCD),
                    ByteUtil.bcdToInt(unit.data, 44 + offset, 2, false) / 100,
                    unit.data[45 + offset] & 0xff, unit.data[46 + offset]
            );
            return data;
        } catch (Exception e) {
            //TODO

        }
        return null;
    }

    private ValveVO valveData(RunaFrameSequence.DataUnit unit, int offset) {
        ValveVO valveData = new ValveVO();
        try {
            valveData.setReadTime(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
            valveData.setIndex(unit.pn);
            boolean belock = unit.data[6 + offset] == 4;
            if (belock) {
                valveData.setOpenLock((unit.data[11 + offset] & 0xff) == 1);
                valveData.setRemoteSet((float) (unit.data[12 + offset] & 0xff));//目前都是开度，先写
                valveData.setStatus(ByteUtil.byteToHexString(unit.data, 13 + offset, 2));
                if ("FFFF".equals(valveData.getStatus())) {
                    valveData.setSuccess(false);
                } else {
                    byte bStatus = unit.data[13 + offset];
                    valveData.setFaultCode(FaultUtil.runaEquValveStatusFaults(valveData.getStatus()));
                    valveData.setActOpen((float) ((bStatus & 0b11) == 0 ? 100 : 0));
                }
                valveData.setActOpen((float) (unit.data[15] == (byte) 0xff ? -1 : unit.data[15]));
                valveData.setSoftVersion(Integer.toHexString(unit.data[16] & 0xff));
                return valveData;
            }
            boolean have2c = unit.data[6 + offset] == 2;
            valveData.setOpenTime(Float.valueOf(ByteUtil.byteToInt(unit.data, 11 + offset, ByteOrder.DCBA)));
            valveData.setOpenUL((float) (unit.data[28 + offset] & 0xff));
            valveData.setOpenLock((unit.data[29 + offset] & 0xff) == 1);
            valveData.setRemoteSet((float) (unit.data[30 + offset] & 0xff));
            valveData.setTempLock((unit.data[31 + offset] & 0xff) == 1);
            valveData.setTempDL((float) ByteUtil.bcdToInt(unit.data, 34 + offset, 2, false) / 100);
            valveData.setTempUL((float) ByteUtil.bcdToInt(unit.data, 36 + offset, 2, false) / 100);
            valveData.setSupplyTemp((float) ByteUtil.bcdToInt(unit.data, 52 + offset, 2, false) / 100);
            valveData.setReturnTemp((float) ByteUtil.bcdToInt(unit.data, 54 + offset, 2, false) / 100);
            valveData.setValveDate(sdfymdhms.parse(ByteUtil.bcd2str(unit.data, 56 + offset, 6, false)));
            valveData.setStatus(ByteUtil.byteToHexString(unit.data, 62 + offset, 2));
            valveData.setActOpen((float) (unit.data[64 + offset] & 0xff));
            if (valveData.getStatus().substring(1,2).equalsIgnoreCase("0")) {
                valveData.setActOpen((float) 100);
                valveData.setActValue((float) 100);
            }
            valveData.setHardVersion(Integer.toHexString(unit.data[71 + offset] & 0xff));
            AffiliatePanelVO panel = new AffiliatePanelVO(unit.data[76 + offset] & 0xff, unit.data[77 + offset] & 0xff,
                    (float) ByteUtil.bcdToInt(unit.data, 78 + offset, 2, false) / 100,
                    ByteUtil.byteToShort(unit.data, 90 + offset, ByteOrder.AB));
            if (valveData.getValveSerial() != null)
                panel.setSerial(valveData.getValveSerial());
            if (valveData.getIndex() != null)
                panel.setValveIndex(valveData.getIndex());
            panel.setReadTime(valveData.getReadTime());
            valveData.setPanelVOList(Collections.singletonList(panel));
            if ("FFFF".equals(valveData.getStatus())) {
                valveData.setSuccess(false);
            } else {
                byte bStatus = unit.data[62 + offset];
//                valveData.setSwitchState((bStatus & 0b11) == 0);
                valveData.setFaultCode(FaultUtil.runaEquValveStatusFaults(valveData.getStatus()));
            }
            return valveData;
        } catch (Exception e) {

        }
        return null;
    }


    private PanelVO measurTemperaturePanelData(RunaFrameSequence.DataUnit unit, int offset) {
        PanelVO panelData = new PanelVO();
        try {
            panelData.setValveIndex(unit.pn);
            panelData.setReadTime(sdfymdhm.parse(ByteUtil.bcd2str(unit.data, offset, 5, false)));
//            panelData.setStandby1(ByteUtil.byteToInt(unit.data, 5+offset, ByteOrder.ABCD));
//            panelData.setStandby1(ByteUtil.byteToInt(unit.data, 9+offset, ByteOrder.ABCD));
//            panelData.setCollectorAddr(new String(unit.data, 13+offset, 4));
            panelData.setPanelDate(sdfymdhms.parse(ByteUtil.bcd2str(unit.data, 17 + offset, 6, false)));
            //保留3, +3字节
//            panelData.setReportPeriod(unit.data[26+offset]&0xff);
            panelData.setTemperature((float) ByteUtil.bcdToInt(unit.data, 27 + offset, 2, false) / 100);
            panelData.setCounter(unit.data[29 + offset] & 0xff);
            panelData.setStatus(Integer.toHexString(unit.data[30 + offset] & 0xff));
            if ((unit.data[30 + offset] & 0xff) == 0xff) {
                panelData.setSuccess(false);
            } else {
                ArrayList<String> list = new ArrayList<>();
                if ((unit.data[30 + offset] & 0xff >> 3 & 1) == 1) {
//                    faults.append("电池欠压,");
                    list.add("电池欠压");
                }
                if ((unit.data[30 + offset] & 0xff >> 7 & 1) == 1) {
//                    faults.append("无线异常,");
//                    list.add("无线异常");
                }
                panelData.setFaultCode(list);
            }
            return panelData;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private Record record(RunaFrameSequence.DataUnit unit) {
        Record record = new Record();
        record.setPort(unit.data[1] & 0xff);
        record.setProtocol(unit.data[2] & 0xff);
        record.setAddress(ByteUtil.bcdToInt(unit.data, 3, 6, false));
        record.setInhabitant(ByteUtil.bcdToInt(unit.data, 9, 6, false));
        record.setArea(ByteUtil.bcdToInt(unit.data, 15, 2, false) / 10);
        record.setRate(unit.data[17]);
        record.setType(unit.data[18]);
        return record;
    }

    private HeatMeterRecordData heatMeterRecordData(RunaFrameSequence.DataUnit unit) {
        HeatMeterRecordData recordData = new HeatMeterRecordData();
        recordData.setRecord(record(unit));
        recordData.setHeatMeterData(heatMeterData(unit, 21));
        return recordData;
    }

    private WaterMeterRecordData waterMeterRecordData(RunaFrameSequence.DataUnit unit) {
        WaterMeterRecordData recordData = new WaterMeterRecordData();
        recordData.setRecord(record(unit));
        recordData.setWaterMeterData(waterMeterData(unit, 21));
        return recordData;
    }

    private ElectricityMeterRecordData electricityMeterRecordData(RunaFrameSequence.DataUnit unit) {
        ElectricityMeterRecordData recordData = new ElectricityMeterRecordData();
        recordData.setRecord(record(unit));
        recordData.setElectricityMeterData(electricityMeterData(unit, 21));
        return recordData;
    }

    private ValveRecordData valveRecordData(RunaFrameSequence.DataUnit unit) {
        ValveRecordData recordData = new ValveRecordData();
        recordData.setRecord(record(unit));
        recordData.setValveData(valveData(unit, 21));
        return recordData;
    }

    private TemperatureCollectorRecordData temperatureCollectorRecordData(RunaFrameSequence.DataUnit unit) {
        TemperatureCollectorRecordData recordData = new TemperatureCollectorRecordData();
        recordData.setRecord(record(unit));
        recordData.setTemperatureCollectorData(temperatureCollectorData(unit, 21));
        return recordData;
    }

    private MeasurTemperaturePanelRecordData measurTemperaturePanelRecordData(RunaFrameSequence.DataUnit unit) {
        MeasurTemperaturePanelRecordData recordData = new MeasurTemperaturePanelRecordData();
        recordData.setRecord(record(unit));
        recordData.setMeasurTemperaturePanelData(measurTemperaturePanelData(unit, 21));
        return recordData;
    }

    private PublicBuildingsOrUnitValveRecordData publicBuildingsOrUnitValveRecordData(RunaFrameSequence.DataUnit unit) {
        PublicBuildingsOrUnitValveRecordData recordData = new PublicBuildingsOrUnitValveRecordData();
        recordData.setRecord(record(unit));
        recordData.setData(publicBuildingsOrUnitValveData(unit, 21));
        return recordData;
    }

    private ExtendData extendCodeData(RunaFrameSequence.DataUnit unit) {
        ExtendData extendData = new ExtendData();
        extendData.setCode(ByteUtil.byteToShort(unit.data));
        switch (extendData.getCode()) {
            case 1:
            case 2:
                extendData.setIndexEnable(ByteUtil.byteToHexString(unit.data, 2, 128));
                break;
            case 3:
                extendData.setIndexDisable(ByteUtil.byteToHexString(unit.data, 2, 128));
                break;
            case 4:
                String iccId = new String(unit.data, 2, 32).trim();
                extendData.setIccid(iccId);
                break;
            case 5:
                extendData.setSpiFlashID(ByteUtil.byteToHexString(unit.data, 2, 8));
                break;
            case 6:
                extendData.setSDCardCaptity(ByteUtil.byteToLong(unit.data, 2, ByteOrder.HGFEDCBA));
                break;
            case 31:
                extendData.setBatteryVol(ByteUtil.byteToUShort(unit.data, 2, ByteOrder.BA).intValue());
                break;
        }
        return extendData;
    }

    private TerminalDayPowerTime terminalDayPowerTime(RunaFrameSequence.DataUnit unit) {
        TerminalDayPowerTime time = new TerminalDayPowerTime();
        try {
            time.setDate(sdfymd.parse(ByteUtil.bcd2str(unit.data, 0, 3, false)));
            time.setMinute(ByteUtil.byteToShort(unit.data, 3, ByteOrder.BA));
            time.setResetTime(ByteUtil.byteToShort(unit.data, 5, ByteOrder.BA));
            return time;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private TerminalMonthPowerTime terminalMonthPowerTime(RunaFrameSequence.DataUnit unit) {
        TerminalMonthPowerTime time = new TerminalMonthPowerTime();
        try {
            time.setDate(sdfym.parse(ByteUtil.bcd2str(unit.data, 0, 2, false)));
            time.setMinute(ByteUtil.byteToShort(unit.data, 2, ByteOrder.BA));
            time.setResetTime(ByteUtil.byteToShort(unit.data, 4, ByteOrder.BA));
            return time;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private TerminalDayCommFlow terminalDayCommFlow(RunaFrameSequence.DataUnit unit) {
        TerminalDayCommFlow flow = new TerminalDayCommFlow();
        try {
            flow.setDate(sdfymd.parse(ByteUtil.bcd2str(unit.data, 0, 3, false)));
            flow.setCommFlow(ByteUtil.byteToInt(unit.data, 3, ByteOrder.DCBA));
            return flow;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private TerminalMonthCommFlow terminalMonthCommFlow(RunaFrameSequence.DataUnit unit) {
        TerminalMonthCommFlow flow = new TerminalMonthCommFlow();
        try {
            flow.setDate(sdfymd.parse(ByteUtil.bcd2str(unit.data, 0, 3, false)));
            flow.setCommFlow(ByteUtil.byteToInt(unit.data, 3, ByteOrder.DCBA));
            return flow;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private HeatMeterFreezeData heatMeterDayFreezeData(RunaFrameSequence.DataUnit unit) {
        HeatMeterFreezeData freezeData = new HeatMeterFreezeData();
        try {
            freezeData.setDate(sdfymd.parse(ByteUtil.bcd2str(unit.data, 0, 3, false)));
            freezeData.setFreezeType(FreezeType.DAY);
            freezeData.setHeatMeterData(heatMeterData(unit, 3));
            return freezeData;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private HeatMeterFreezeData heatMeterMonthFreezeData(RunaFrameSequence.DataUnit unit) {
        HeatMeterFreezeData freezeData = new HeatMeterFreezeData();
        try {
            freezeData.setDate(sdfym.parse(ByteUtil.bcd2str(unit.data, 0, 2, false)));
            freezeData.setFreezeType(FreezeType.MONTH);
            freezeData.setHeatMeterData(heatMeterData(unit, 2));
            return freezeData;
        } catch (Exception e) {
            //TODO
        }
        return null;
    }

    private HeatMeterFreezeData heatMeterHourFreezeData(RunaFrameSequence.DataUnit unit) {
        HeatMeterFreezeData freezeData = new HeatMeterFreezeData();
        try {
            freezeData.setDate(sdfymdhms.parse(ByteUtil.bcd2str(unit.data, 0, 4, false)));
            freezeData.setFreezeType(FreezeType.HOUR);
            freezeData.setHeatMeterData(heatMeterData(unit, 4));
        } catch (Exception e) {
            //TODO
        }
        return freezeData;
    }

}
